////////////////////////////////////////////////////////////////////////////
//
//  Crytek Engine Source File.
//  Shader extension
//  Copyright (C), Crytek Studios, 2001-2004.
// -------------------------------------------------------------------------
//  File name:   CommonCausticsPass.cfi
//  Version:     v1.00
//  Created:     11/08/2006 by Tiago Sousa
//  Compilers:   
//  Description: Common/Shared passes bettwen shaders. Should be included after
//    main shader technique
//
//  NOTE: ALWAYS MAKE SURE VERTEX INPUTS MATCH CORRECTLY BEFORE INCLUDING THIS!
//  TODO: Add support for texture modifiers, since Illum and others require it
//  "Common_" name convention - for shader sharing in cache files
//
//
// -------------------------------------------------------------------------
////////////////////////////////////////////////////////////////////////////


////////////////////////////////////////////////////////
//                Caustics Pass
////////////////////////////////////////////////////////

half4 CausticParams	: PB_CausticsParams < vsregister = VS_REG_PB_8; >;  // xy: caustics distance, zw: 1 / caustics distance
half4 CausticsAABB < vsregister = VS_REG_PB_9; > = { PB_FromRE[0], PB_FromRE[1], PB_FromRE[2], PB_FromRE[3] };
half4 CausticSmoothSunDir	: PB_CausticsSmoothSunDirection; 

sampler2D wavesSampler = sampler_state
{
  Texture = EngineAssets/Textures/oceanwaves_ddn.dds;
  MinFilter = LINEAR;
  MagFilter = LINEAR;
  MipFilter = LINEAR;
  AddressU = Wrap;
  AddressV = Wrap;	
};

sampler2D causticsSampler = sampler_state
{
  Texture = EngineAssets/Textures/caustics_sampler.dds;
  MinFilter = LINEAR;
  MagFilter = LINEAR;
  MipFilter = NONE;
  AddressU = Clamp;
  AddressV = Clamp;	
};

float4 g_fWaterLevel : PB_WaterLevel;


///////////////// vertex input/output //////////////////
struct vert2fragCaustics
{
  OUT_P
  float4 baseTC    : TEXCOORDN; // zw unused
  float4 waveTC      : TEXCOORDN;
  float4 causticTC0  : TEXCOORDN;
  float4 causticTC1  : TEXCOORDN;
  
  float4 vPosition : TEXCOORDN;  // w: caustics attenuation
  half4 vSunTS    : TEXCOORDN;  // w: caustics highlight attenuation

  float4 screenProj : TEXCOORDN;
  
  float4 Color      : COLOR0;
};

///////////////// vertex shaders //////////////////
vert2fragCaustics Common_CausticsPassVS(app2vertZGeneral IN_common)
{
  vert2fragCaustics OUT = (vert2fragCaustics)0;

	app2vertZGeneral IN = IN_common;
#if TEMP_TERRAIN
	IN.vertCommon.Position.z = IN_common.vertCommon.baseTC.x;
#endif

  VSVertexContext vertPassPos = (VSVertexContext)0;
  streamPos_FromZ(IN, vertPassPos);

  float3x3 tangentToWS = 0;
#if TEMP_TERRAIN
  OUT.HPosition = Pos_VS_Terrain(0.001f, g_VS_ViewProjZeroMatr, vertPassPos);
  float3 vNorm = vertPassPos.Normal;
  
  // get tangent space info from texgen parameters
  float3 Tangent = normalize(LayerTexGen[0].xyz);
  float3 Binormal = normalize(LayerTexGen[1].xyz);
  
  // project tangent and binormal to plane perpendicular to the normal
  Tangent-=dot(Tangent,vNorm)*vNorm;
  Binormal-=dot(Binormal,vNorm)*vNorm;
  Tangent=normalize(Tangent);
  Binormal=normalize(Binormal);

  tangentToWS[0].xyz = Tangent;    
  tangentToWS[1].xyz = Binormal;
  tangentToWS[2].xyz = vNorm; 
  
#else  
  OUT.HPosition = Pos_VS_General(g_VS_ViewProjZeroMatr, vertPassPos);
 
  // Output world to tangent matrix and world space position  
  float3 worldTangentS = normalize( mul((const float3x3)vertPassPos.InstMatrix, vertPassPos.ObjToTangentSpace[0]) );
  float3 worldTangentT = normalize( mul((const float3x3)vertPassPos.InstMatrix, vertPassPos.ObjToTangentSpace[1]) );
  float3 worldTangentN = (cross(worldTangentS, worldTangentT)) * vertPassPos.Tangent.w;
  tangentToWS[0].xyz = (worldTangentS); 
  tangentToWS[1].xyz = (worldTangentT);
  tangentToWS[2].xyz = (worldTangentN);
#endif
  
  // output sun direction in tangent space
  OUT.vSunTS.xyz = mul( g_VS_SunLightDir.xyz, tangentToWS );
       
 float4 baseTC = 0;
#if !_RT_OBJ_IDENTITY
  vertPassPos.WorldPos.xyz += g_VS_WorldViewPos.xyz;
#endif
 
 //mat._m00_m10_m20_m30
 ///float3 vAABBCenter = float3( InstMatrix._m03_m13_m23 );
 float3 vAABBCenter = float3(vertPassPos.InstMatrix[0].w, vertPassPos.InstMatrix[1].w, vertPassPos.InstMatrix[2].w );
 float4 vAABBMin = mul(vertPassPos.InstMatrix, CausticsAABB);
#if !_RT_OBJ_IDENTITY
 vAABBMin.xyz += g_VS_WorldViewPos.xyz;
 vAABBCenter.xyz += g_VS_WorldViewPos.xyz;
#endif
 
#if !TEMP_TERRAIN
 baseTC = vertPassPos.baseTC;  
#else 
#if CAFE 
  baseTC.xy = mul((float3x3)LayerTexGen, vertPassPos.WorldPos.xyz).xy;
#else
  baseTC.xy = mul((float2x3)LayerTexGen, vertPassPos.WorldPos.xyz);
#endif
  baseTC.z = 0;
  baseTC.w = vertPassPos.Position.w;     
#endif    

  OUT.vPosition = vertPassPos.WorldPos.xyzz;
  
#if !TEMP_TERRAIN
  float fRadius = length(vAABBCenter.xyz - vAABBMin.xyz )*0.8;
  // saturate(fRadius*1000)
  OUT.vPosition.w += fRadius;
  
  // Pull up plane at same time as the center goes bellow water plane 
  // (to compensate for activating caustics when center goes bellow water plane)
  OUT.vPosition.w -= abs(vAABBCenter.z - g_fWaterLevel.z)*0.9;
  
#endif  
  
  // Generate projection matrix based on sun direction  
  float3 dirZ = -CausticSmoothSunDir.xyz;
  float3 up = float3(0,0,1);
  float3 dirX = normalize( cross(up, dirZ) );
  float3 dirY = normalize( cross(dirZ, dirX) );

  float3x3 mLightView;
  mLightView[0] = dirX.xyz;
  mLightView[1] = dirY.xyz;
  mLightView[2] = dirZ.xyz;

  // Get uvs    
  float3 vProj = mul(mLightView, OUT.vPosition.xyz);

  //CausticAnimGenParams.w = 0;
  // Output caustics procedural texture generation 
  OUT.waveTC.xy =  vProj.xy * 2 * 0.01 * 0.012 + g_VS_AnimGenParams.w * 0.06;
  OUT.waveTC.wz =  vProj.xy * 2 * 0.01 * 0.01 + g_VS_AnimGenParams.w * 0.05;

#if TEMP_TERRAIN  
  float4 Color = vertPassPos.Color;
#if !_RT_OBJ_IDENTITY
	if (Color.g > 127.f/255.f)
		Color.g -= 128.f/255.f;
#endif

  OUT.causticTC0.xy =  vProj.xy * 0.01 * 0.5 + g_VS_AnimGenParams.w * 0.1;
  OUT.causticTC0.wz =  vProj.yx * 0.01 * 0.5 - g_VS_AnimGenParams.w * 0.11;  

  OUT.causticTC1.xy =  vProj.xy * 0.01 * 2.0 + g_VS_AnimGenParams.w * 0.1;
  OUT.causticTC1.wz =  vProj.yx * 0.01 * 2.0 - g_VS_AnimGenParams.w * 0.11;  
  
  // Output vertex weight: needed for avoiding seams bettween terrain layers
  float fWeight = saturate( 1 - abs(Color.g*255 - LayerTexGen[2].w) );
  float3 NormalAbs = abs(tangentToWS[2].xyz);
  
  if(NormalAbs.x>=NormalAbs.y && NormalAbs.x>=NormalAbs.z)
		fWeight *= LayerTexGen[2].x;
  else if(NormalAbs.y>=NormalAbs.x && NormalAbs.y>=NormalAbs.z)
		fWeight *= LayerTexGen[2].y;
	else
		fWeight *= LayerTexGen[2].z;
  
  OUT.Color = fWeight;
    
#else
  
  OUT.causticTC0.xy =  vProj.xy * 0.01  + g_VS_AnimGenParams.w * 0.1;
  OUT.causticTC0.wz =  vProj.yx * 0.01 - g_VS_AnimGenParams.w * 0.11;  

  OUT.causticTC1.xy =  vProj.xy * 0.01 * 4.0 + g_VS_AnimGenParams.w * 0.1;
  OUT.causticTC1.wz =  vProj.yx * 0.01 * 4.0 - g_VS_AnimGenParams.w * 0.11;  

#endif  
   
  _TCModify(baseTC, OUT.baseTC, vertPassPos.Position, vertPassPos.ObjToTangentSpace[2], TS_DIFFUSE);   

  // Note: this used to be computer per-pixel. Did some tests don't see much diference.
  const half cMaxHightVis = 8.0;
  half fHighlightAtten =  cMaxHightVis / (g_fWaterLevel.z - OUT.vPosition.w);                         // 2 alu    
  fHighlightAtten = saturate( fHighlightAtten ) * saturate(abs( fHighlightAtten ));

  half fAtten =  (g_fWaterLevel.z - OUT.vPosition.w);
  half fDistToCam = length( g_VS_WorldViewPos.xyz - OUT.vPosition.xyz );                                 // 2 alu
  
  // 4 alu
  fAtten = saturate(fAtten) * saturate( CausticParams.x /( 0.075 * fDistToCam * fDistToCam ) );     // todo:  pass attenuation factor per constant - maybe affected by fog params  


  OUT.vPosition.w = fAtten;
  OUT.vSunTS.w = 1;//fHighlightAtten;

  OUT.screenProj = HPosToScreenTC(OUT.HPosition);
    	    
  return OUT;            
}

///////////////// pixel shaders //////////////////

pixout Common_CausticsPassPS(vert2fragCaustics IN)
{
  pixout OUT = (pixout)0;
      
#if PS3  
  #pragma sce-cgc("-texformat default COMPRESSED_RGBA_S3TC_DXT5");
#endif

  half2 bumpColor = tex2D(bumpMapSampler, IN.baseTC.xy).xy;                                    // 3 alu

 #if %_RT_DEBUG0 || %_RT_DEBUG1 || %_RT_DEBUG2 || %_RT_DEBUG3
   DebugOutput(OUT.Color, IN.baseTC);
   return OUT;
 #endif
  
  // break movement, with random patterns
  float2 wave = FetchNormalMap( wavesSampler, IN.waveTC.xy).xy;                                                
  wave.xy += FetchNormalMap( wavesSampler, IN.waveTC.wz).xy;                                                 // 1 alu
  
  // put in normalized range (with constant scale)
  wave.xy = wave.xy*0.04 - 0.04;                                                                    // 1 alu
  
  half3 causticMapR = 1.0;

  causticMapR.xy = FetchNormalMap( wavesSampler, IN.causticTC0.xy + wave.xy);     // 1 tex + 2 alu
  causticMapR.xy += FetchNormalMap( wavesSampler, IN.causticTC0.wz + wave.xy).xy;     // 1 tex + 3 alu
  
  half2 causticHighFreq = 0;
  causticHighFreq = FetchNormalMap( wavesSampler, IN.causticTC1.xy + wave.xy ).xy;   // 1 tex  + 1 alu
  causticHighFreq += FetchNormalMap( wavesSampler, IN.causticTC1.wz + wave.xy ).xy;   // 1 tex  + 2 alu

  causticMapR.xy = (causticMapR.xy + causticHighFreq * 2.0h - 3.0h);
  
  //causticMapR = ( causticMapR + bumpColor * 0.25) ;                                                   // 1 alu
  causticMapR.xy = ( causticMapR.xy + bumpColor.xy * 0.25h) ;     // just perturb xy, saves about 5 alu
    
  // Get main vectors/coeficients	       
  half3 normalVec = normalize( causticMapR );       // 3 alu
  half3 vSunTS = normalize( IN.vSunTS.xyz );        // 3 alu

  half fAtten = IN.vPosition.w;
  half fHighlightAtten = IN.vSunTS.w;
   
  // about 10 alu
  half2 vRefrR = refract( vSunTS.xyz, normalVec.xyz, 0.9 ).xy;                              // Displace refraction vectors to simulate color dispersion   
  half2 vRefrG = vRefrR - normalVec.xy * 0.05; 
  half2 vRefrB = vRefrR - normalVec.xy * 0.1;   
  
  // 3 alu
  // Caustics sampler contains function: abs( 1-(abs( a) + abs(b))*0.5 ), which generates nice sharp pattern  
  half3 cCaustic = half3( tex2D(causticsSampler, vRefrR.xy*0.5+0.5).x,
                            tex2D(causticsSampler, vRefrG.xy*0.5+0.5).x,
                            tex2D(causticsSampler, vRefrB.xy*0.5+0.5).x );

  half3 cCausticOrig = cCaustic;  
  
  
  // Power-up caustic to make it sharper, and apply sun color                                       // 3 alu
  cCaustic = pow( cCaustic, 16 ) * fHighlightAtten;
  OUT.Color.xyz = saturate( dot(  normalVec.xyz, CausticSmoothSunDir.xyz ) ) * cCaustic * g_PS_SunColor * 0.5;     // 4 alu
  
  // Apply attenuation, and no caustics in backfaces  
  OUT.Color.xyz *= saturate( dot( vSunTS.xyz, normalVec.xyz ) + 1.0);
    
  // Darken base stuff, to fake some shading
#if TEMP_TERRAIN  

  OUT.Color.w =   IN.Color.w * saturate( dot(1 - cCausticOrig, 0.25 *CausticParams.z) );                                                        // 2 alu
  
#else                 

  OUT.Color.w = saturate( dot(1 - cCausticOrig, 0.33 *CausticParams.z) );                                                        // 2 alu
  
#endif  

  OUT.Color.xyz *= CausticParams.y * PS_HDR_RANGE_ADAPT_MAX;
  
  OUT.Color *= fAtten;                                                                              // 1 alu

#if PS3
  //if %_RT_HDR_MODE 
    // Custom blending for PS3 (decode and blend)
    half4 cDstRT = DecodeHDRBuffer( tex2Dproj( HDRTargetEncodedSampler, IN.screenProj.xyzw ) ); 
    OUT.Color += cDstRT * (1 - OUT.Color.w );

    OUT.Color = EncodeHDRBuffer( OUT.Color );
  //endif
#endif
  
  return OUT;
}

//////////////////////////////// technique ////////////////

technique CausticsPass
{
  pass p0
  {
    VertexShader = Common_CausticsPassVS() CausticsVS;
    PixelShader = Common_CausticsPassPS() CausticsPS;
    
    ZEnable = true;
    ZWriteEnable = false;
    ZFunc = Equal;
    CullMode = Back;        
    
#if TEMP_TERRAIN
    ZFunc = LEqual;
#endif
                         
    SrcBlend = ONE;
    DestBlend = ONE_MINUS_SRC_ALPHA;
    AlphaBlendEnable = true;

    IgnoreMaterialState = true;
     
#if %GRASS || %LEAVES || %HAIR_PASS
    CullMode = None;
#endif

  }
}


